﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DivaScript;
using ft_universal_parser;
using MikuMikuModel.FarcPack;
using ScriptUtilities;
using MikuMikuLibrary;
using MikuMikuLibrary.Sprites;
using MikuMikuLibrary.Textures;
using MikuMikuLibrary.Databases;
using System.Drawing;
using MikuMikuLibrary.Archives.Farc;

namespace ft_module_parser
{
    class divapvmod
    {
        pv_dbs_raw pv_orig = new pv_dbs_raw();
        pv_fields pv_field = new pv_fields();
        pv_lists_raw pv_list = new pv_lists_raw();
        SpriteDatabase spritedb = new SpriteDatabase();
        public void RestoreDb(bool skipuserinput = false, bool isConsole = false)
        {
            if (File.Exists(@"rom\pv_db.bak"))
            {
                File.Copy("rom\\pv_db.bak", "rom\\pv_db.txt", true);
                File.Delete(@"rom\pv_db.bak");
            }

            if (File.Exists(@"rom\pv_field.bak"))
            {
                File.Copy("rom\\pv_field.bak", "rom\\pv_field.txt", true);
                File.Delete(@"rom\pv_field.bak");
            }

            if (!isConsole)
            {
                if (File.Exists(@"rom\gm_pv_list_tbl.bak"))
                {
                    File.Copy("rom\\gm_pv_list_tbl.bak", "rom\\gm_pv_list_tbl.farc", true);
                    File.Delete(@"rom\gm_pv_list_tbl.bak");
                }
            }
            Console.WriteLine("DB Restored!");
            if (!skipuserinput) Console.ReadKey();
        }

        //gametype 0 = ac
        //gametype 1 = aft
        public void ApplyMods(int gametype = 0, string gamepath = "", bool forcemod = false, bool isConsole = false)
        {
            pv_list.game_type = gametype;

            if (gamepath.Count() != 0)
                gamepath = gamepath + "\\";
            if (!File.Exists(gamepath + @"rom\pv_db.bak"))
            {
                if (!forcemod)
                {
                    Console.WriteLine("Welcome to lyb's DivaAutoMod tool v3");
                    if (gametype == 0)
                    {
                        Console.WriteLine("Before we get started, this tool currently does not support cleaning the files back.");
                        Console.WriteLine("So it is recommended to use a make another copy of the game or delete the modded files manually.");
                        Console.WriteLine("However, you can restore the DB so the modded pv won't show up by running this software with \"-restore\" parameter");
                        Console.WriteLine("If you agree with this behaviour, type \"AGREE\" in full caps.");
                        Console.WriteLine("This message will no longer show up once you Agree.");
                    }
                    if (gametype == 1)
                    {
                        Console.WriteLine("Before we get started, this tool will modify `mdata\\M999` folder. ");
                        Console.WriteLine("Make sure there are no other mods using it.");
                        Console.WriteLine("If you agree with this behaviour, type \"AGREE\" in full caps.");
                        Console.WriteLine("This message will no longer show up once you Agree.");
                    }

                    Console.Write("Input=");
                    var inp = Console.ReadLine();
                    if (inp != "AGREE")
                    {
                        Environment.Exit(0);
                    }
                }
                File.Copy(gamepath + "rom\\pv_db.txt", gamepath + "rom\\pv_db.bak");
            }

            if (gametype == 1)
            {
                if (!Directory.Exists(gamepath + "mdata\\M999"))
                    Directory.CreateDirectory(gamepath + "mdata\\M999");

                if (!Directory.Exists(gamepath + "mdata\\M999\\rom"))
                    Directory.CreateDirectory(gamepath + "mdata\\M999\\rom");

                if (!Directory.Exists(gamepath + "mdata\\M999\\rom\\auth_3d"))
                    Directory.CreateDirectory(gamepath + "mdata\\M999\\rom\\auth_3d");

                if (!Directory.Exists(gamepath + "mdata\\M999\\rom\\rob"))
                    Directory.CreateDirectory(gamepath + "mdata\\M999\\rom\\rob");

                if (!Directory.Exists(gamepath + "mdata\\M999\\rom\\script"))
                    Directory.CreateDirectory(gamepath + "mdata\\M999\\rom\\script");

                if (!Directory.Exists(gamepath + "mdata\\M999\\rom\\objset"))
                    Directory.CreateDirectory(gamepath + "mdata\\M999\\rom\\objset");

                if (!Directory.Exists(gamepath + "mdata\\M999\\rom\\movie"))
                    Directory.CreateDirectory(gamepath + "mdata\\M999\\rom\\movie");

                if (!Directory.Exists(gamepath + "mdata\\M999\\rom\\sound"))
                    Directory.CreateDirectory(gamepath + "mdata\\M999\\rom\\sound");

                if (!Directory.Exists(gamepath + "mdata\\M999\\rom\\2d"))
                    Directory.CreateDirectory(gamepath + "mdata\\M999\\rom\\2d");

                if (!Directory.Exists(gamepath + "mdata\\M999\\rom\\sound\\song"))
                    Directory.CreateDirectory(gamepath + "mdata\\M999\\rom\\sound\\song");

                //Logs.WriteLine("### MDATA LOAD ###");
                mdataUtilities.getMdatas(gamepath + @"mdata\");
                for (int i = 0; i < 7; i++)
                {
                    mdataUtilities.getPath(gamepath + @"mdata\", (mdataUtilities.dbType)i);
                }
                //Logs.WriteLine(mdataUtilities.getPath(acpath + @"\mdata", (mdataUtilities.dbType)i));
                //Logs.WriteLine("### MDATA DONE ###");
            }

            if (!File.Exists(gamepath + @"rom\pv_field.bak"))
                File.Copy(gamepath + "rom\\pv_field.txt", gamepath + "rom\\pv_field.bak");

            if (!isConsole)
                if (!File.Exists(gamepath + @"rom\gm_pv_list_tbl.bak"))
                    File.Copy(gamepath + "rom\\gm_pv_list_tbl.farc", gamepath + "rom\\gm_pv_list_tbl.bak");

            pv_orig.load(gamepath + @"rom\pv_db.bak");

            if (gametype == 1)
                pv_orig.loadmdata(mdataUtilities.getPath(gamepath + "mdata", mdataUtilities.dbType.pv_db));

            pv_field.load(gamepath + @"rom\pv_field.bak");
            if (!isConsole)
                Tools.Extract(gamepath + @"rom\gm_pv_list_tbl.bak");
            if (!isConsole)
                pv_list.load(gamepath + @"rom\gm_pv_list_tbl\gm_pv_list.bin");

            if (gametype == 1)
                spritedb.Load(mdataUtilities.getPath(gamepath + "mdata", mdataUtilities.dbType.spr_db));

            foreach (string file in Directory.EnumerateFiles(gamepath + "divamods\\", "mod.txt", SearchOption.AllDirectories))
            {
                string name = "";
                string author = "";
                int ver = 0;
                int pvid = -1;
                string type = "";
                string base_name = "";
                var path = Path.GetDirectoryName(file);
                using (StreamReader modtxt = new StreamReader(file))
                {
                    while (modtxt.Peek() >= 0)
                    {
                        var line = modtxt.ReadLine();
                        if (!line.StartsWith("#"))
                        {
                            int index = line.IndexOf('=');
                            string first = line.Substring(0, index);
                            string second = line.Substring(index + 1);

                            if (first == "name")
                                name = second;

                            if (first == "author")
                                author = second;

                            if (first == "ver")
                                ver = int.Parse(second);

                            if (first == "pvid")
                                pvid = int.Parse(second);

                            if (first == "type")
                                type = second;

                            if (first == "base_name")
                                base_name = second;
                        }
                    }
                }
                //apply dbs (pv_db, gm_pv_list, pv_field)
                Console.WriteLine("Found Mod " + name + " by " + author);

                bool modloaded = false;

                if (ver == 1)
                {
                    modloaded = true;

                    int id = pv_orig.merge(path + "\\pv_db.txt", pv_list);
                    Console.WriteLine("Created new PV ID at " + id);

                    if (File.Exists(path + "\\pv_field.txt"))
                        pv_field.merge(path + "\\pv_field.txt", id);

                    if (gametype == 1)
                    {
                        //copy other stuff
                        if (Directory.Exists(path + "\\movie"))
                            Tools.CopyFiles(path + "\\movie\\", gamepath + @"mdata\m999\rom\movie\");
                        if (Directory.Exists(path + "\\script"))
                            Tools.CopyFiles(path + "\\script\\", gamepath + @"mdata\m999\rom\script\");
                        if (Directory.Exists(path + "\\song"))
                            Tools.CopyFiles(path + "\\song\\", gamepath + @"mdata\m999\rom\sound\song\");
                    }
                    else
                    {
                        //copy other stuff
                        if (Directory.Exists(path + "\\movie"))
                            Tools.CopyFiles(path + "\\movie\\", gamepath + @"rom\movie\");
                        if (Directory.Exists(path + "\\script"))
                            Tools.CopyFiles(path + "\\script\\", gamepath + @"rom\script\");
                        if (Directory.Exists(path + "\\song"))
                            Tools.CopyFiles(path + "\\song\\", gamepath + @"rom\sound\song\");
                    }


                }

                if ((ver == 2) || (ver == 3))
                {
                    modloaded = true;

                    if (type == "edit_2nd")
                    {
                        try
                        {
                            ValueTuple<List<PvCommand>, PvDatabaseInfo> ftEditCommands = Diva2ndEditScript.GetFtEditCommands(new EditMode.Diva2nd.Edit(path + "//SECURE.bin"), 000);
                            List<PvCommand> pvCommandList = ftEditCommands.Item1;
                            PvDatabaseInfo pvDatabaseInfo = ftEditCommands.Item2;
                            if (pvCommandList == null || pvDatabaseInfo == null)
                                return;
                            var pv_cmd = pvCommandList;

                            var pv_db = (IEnumerable<string>)pvDatabaseInfo.GetDiva2ndPvDbAc(base_name);

                            if (gametype == 1)
                                pv_db = (IEnumerable<string>)pvDatabaseInfo.GetDiva2ndPvDb(base_name);

                            var pv_field = (IEnumerable<string>)pvDatabaseInfo.GetDiva2ndFieldDb();

                            if (!Directory.Exists(path + "\\script"))
                                Directory.CreateDirectory(path + "\\script");

                            using (StreamWriter outp = new StreamWriter(path + "\\pv_db.txt"))
                            {
                                foreach (var i in pv_db)
                                    outp.WriteLine(i);
                                outp.Close();
                            }

                            using (StreamWriter outp = new StreamWriter(path + "\\pv_field.txt"))
                            {
                                foreach (var i in pv_field)
                                    outp.WriteLine(i);
                                outp.Close();
                            }

                            PvScript.WriteBinaryScript(path + "//script//" + base_name + ".dsc", pv_cmd, Format.FT, false);

                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine("--- :PokeKms: ---");
                            Console.WriteLine(ex.ToString());
                            modloaded = false;
                        }
                    }

                    if (type == "edit_f2nd")
                    {

                    }

                    if (modloaded)
                    {
                        int id = pv_orig.merge(path + "\\pv_db.txt", pv_list, pvid);
                        Console.WriteLine("Created new PV ID at " + id);

                        if (File.Exists(path + "\\pv_field.txt"))
                            pv_field.merge(path + "\\pv_field.txt", id);

                        if (File.Exists(path + "\\thumb\\MERGE_D5COMP_0.png"))
                            if (File.Exists(path + "\\thumb\\MERGE_D5COMP_1.png"))
                            {
                                SpriteSet sprite = new SpriteSet();
                                sprite.Load(new MemoryStream(rescb.spr_sel_pv));

                                Bitmap bm0 = new Bitmap(path + "\\thumb\\MERGE_D5COMP_0.png");
                                Bitmap bm1 = new Bitmap(path + "\\thumb\\MERGE_D5COMP_1.png");

                                sprite.TextureSet.Textures[0] = TextureEncoder.Encode(bm0, TextureFormat.DXT5, false);
                                sprite.TextureSet.Textures[0].Name = "MERGE_D5COMP_0";
                                sprite.TextureSet.Textures[1] = TextureEncoder.Encode(bm1, TextureFormat.DXT5, false);
                                sprite.TextureSet.Textures[1].Name = "MERGE_D5COMP_1";

                                MemoryStream dest0 = new MemoryStream();
                                sprite.Save(dest0, true);
                                var farc = new FarcArchive();
                                farc.Add("spr_sel_pv" + String.Format("{0:000}", id) + ".bin", dest0, false);
                                farc.IsCompressed = true;
                                farc.Save(gamepath + @"mdata\m999\rom\2d\" + "spr_sel_pv" + String.Format("{0:000}", id) + ".farc");

                                var maxid = spritedb.SpriteSets.Max(c => c.Id);

                                int maxsprite = 0;
                                int maxtex = 0;
                                foreach (var sprites in spritedb.SpriteSets)
                                {
                                    foreach (var sprite0 in sprites.Sprites)
                                        if (maxid < sprite0.Id) maxid = sprite0.Id;

                                    foreach (var tex0 in sprites.Textures)
                                        if (maxid < tex0.Id) maxid = tex0.Id;
                                }

                                maxid++;

                                var newsprite = new SpriteSetEntry();
                                newsprite.Name = "SPR_SEL_PV" + String.Format("{0:000}", id);
                                newsprite.FileName = "spr_sel_pv" + String.Format("{0:000}", id) + ".bin";
                                newsprite.Id = maxid;
                                maxid++;
                                var sprentry0 = new SpriteEntry();
                                sprentry0.Id = maxid;
                                sprentry0.Index = 0;
                                sprentry0.Name = "SPR_SEL_PV" + String.Format("{0:000}", id) + "_SONG_BG" + String.Format("{0:000}", id);
                                maxid++;
                                var sprentry1 = new SpriteEntry();
                                sprentry1.Id = maxid;
                                sprentry1.Index = 1;
                                sprentry1.Name = "SPR_SEL_PV" + String.Format("{0:000}", id) + "_SONG_JK" + String.Format("{0:000}", id);
                                maxid++;
                                var sprentry2 = new SpriteEntry();
                                sprentry2.Id = maxid;
                                sprentry2.Index = 2;
                                sprentry2.Name = "SPR_SEL_PV" + String.Format("{0:000}", id) + "_SONG_LOGO" + String.Format("{0:000}", id);
                                maxid++;
                                var texentry0 = new SpriteTextureEntry();
                                texentry0.Id = maxid;
                                texentry0.Index = 0;
                                texentry0.Name = "SPRTEX_SEL_PV" + String.Format("{0:000}", id) + "_MERGE_BC5COMP_0";
                                maxid++;
                                var texentry1 = new SpriteTextureEntry();
                                texentry1.Id = maxid;
                                texentry1.Index = 1;
                                texentry1.Name = "SPRTEX_SEL_PV" + String.Format("{0:000}", id) + "_MERGE_D5COMP_0";

                                newsprite.Sprites.Add(sprentry0);
                                newsprite.Sprites.Add(sprentry1);
                                newsprite.Sprites.Add(sprentry2);
                                newsprite.Textures.Add(texentry0);
                                newsprite.Textures.Add(texentry1);

                                spritedb.SpriteSets.Add(newsprite);
                            }

                        if (gametype == 1)
                        {
                            //copy other stuff
                            if (Directory.Exists(path + "\\movie"))
                                Tools.CopyFiles(path + "\\movie\\", gamepath + @"mdata\m999\rom\movie\");
                            if (Directory.Exists(path + "\\script"))
                                Tools.CopyFiles(path + "\\script\\", gamepath + @"mdata\m999\rom\script\", true);
                            if (Directory.Exists(path + "\\song"))
                                Tools.CopyFiles(path + "\\song\\", gamepath + @"mdata\m999\rom\sound\song\");
                        }
                        else
                        {
                            //copy other stuff
                            if (Directory.Exists(path + "\\movie"))
                                Tools.CopyFiles(path + "\\movie\\", gamepath + @"rom\movie\");
                            if (Directory.Exists(path + "\\script"))
                                Tools.CopyFiles(path + "\\script\\", gamepath + @"rom\script\", true);
                            if (Directory.Exists(path + "\\song"))
                                Tools.CopyFiles(path + "\\song\\", gamepath + @"rom\sound\song\");
                        }
                    }
                }

                if (!modloaded) Console.WriteLine("Skipped. This mod probably requires a newer version of lybDivaMods.");
            }

            if (gametype == 1)
            {
                if (!Directory.Exists(gamepath + @"mdata\m999\rom\gm_pv_list_tbl"))
                    Directory.CreateDirectory(gamepath + @"mdata\m999\rom\gm_pv_list_tbl");

                if (!isConsole)
                {
                    pv_list.SaveTo(gamepath + @"mdata\m999\rom\gm_pv_list_tbl\gm_pv_list.bin");
                    Tools.Extract(gamepath + @"mdata\m999\rom\gm_pv_list_tbl");
                }
                pv_orig.SaveTo(gamepath + @"mdata\m999\rom\pv_db.txt");
                pv_field.SaveTo(gamepath + @"mdata\m999\rom\pv_field.txt");
                spritedb.Save(gamepath + @"mdata\m999\rom\2d\mdata_spr_db.bin");
            }
            else

            {
                if (!isConsole)
                {
                    pv_list.SaveTo(gamepath + @"rom\gm_pv_list_tbl\gm_pv_list.bin");
                    Tools.Extract(gamepath + @"rom\gm_pv_list_tbl");
                }
                pv_orig.SaveTo(gamepath + @"rom\pv_db.txt");
                pv_field.SaveTo(gamepath + @"rom\pv_field.txt");

            }
            if (forcemod)
            {
                //delete divamods backup, set current as base
            }
            //done?
        }
    }
}
